//Basic Raycasting code for hitscan, line checks, etc
//Original code written by rdude, revised and optimized by Saxashitter and rdude, further revision by LJ, neither are affiliated with nor do they endorse rdude or this mod.

local function ccw(Ax,Bx,Cx,Ay,By,Cy) //nabbed this from a python guide
    return FixedMul((Cy-Ay), (Bx-Ax)) > FixedMul((By-Ay), (Cx-Ax))
end

local function intersect(Ax,Bx,Cx,Dx,Ay,By,Cy,Dy)
    return ccw(Ax,Cx,Dx,Ay,Cy,Dy) != ccw(Bx,Cx,Dx,By,Cy,Dy) and ccw(Ax,Bx,Cx,Ay,By,Cy) != ccw(Ax,Bx,Dx,Ay,By,Dy)
end

/*local function intersect(l1p1x, l1p2x, l2p1x, l2p2x, l1p1y, l1p2y, l2p1y, l2p2y, seg1, seg2) //figured out by somebody MUCH MUCH smarter than me on the love2d forums
	local a1,b1,a2,b2 = l1p2y-l1p1y, l1p1x-l1p2x, l2p2y-l2p1y, l2p1x-l2p2x
	local c1,c2 = a1*l1p1x+b1*l1p1y, a2*l2p1x+b2*l2p1y
	local det,x,y = a1*b2 - a2*b1
	if det==0 then return false end
	x,y = (b2*c1-b1*c2)/det, (a1*c2-a2*c1)/det
	if seg1 or seg2 then
		if seg1 and not (min(l1p1x,l1p2x) <= x and x <= max(l1p1x,l1p2x) and min(l1p1y,l1p2y) <= y and y <= max(l1p1y,l1p2y)) or
			seg2 and not (min(l2p1x,l2p2x) <= x and x <= max(l2p1x,l2p2x) and min(l2p1y,l2p2y) <= y and y <= max(l2p1y,l2p2y)) then
			return false
		end
	end
	print("intersect")
	return x,y
end*/

local function segmentVsAABB(x1, y1, x2, y2, l, t, r, b)
  -- normalize segment
  local dx, dy = x2 - x1, y2 - y1
  local d = FixedSqrt(FixedMul(dx, dx) + FixedMul(dy, dy))
  if d == 0 then
    return false
  end
  local nx, ny = dx/d, dy/d
  -- minimum and maximum intersection values
  local tmin, tmax = 0, d
  -- x-axis check
  if nx == 0 then
    if x1 < l or x1 > r then
      return false
    end
  else
    local t1, t2 = (l - x1)/nx, (r - x1)/nx
    if t1 > t2 then
      t1, t2 = t2, t1
    end
    tmin = max(tmin, t1)
    tmax = min(tmax, t2)
    if tmin > tmax then
      return false
    end
  end
  -- y-axis check
  if ny == 0 then
    if y1 < t or y1 > b then
      return false
    end
  else
    local t1, t2 = (t - y1)/ny, (b - y1)/ny
    if t1 > t2 then
      t1, t2 = t2, t1
    end
    tmin = max(tmin, t1)
    tmax = min(tmax, t2)
    if tmin > tmax then
      return false
    end
  end
  -- points of intersection
  -- one point
  local qx, qy = x1 + nx*tmin, y1 + ny*tmin
  if tmin == tmax then
	print("1")
    return true, qx, qy
  end
  -- two points
  print("2")
  return true, qx, qy, x1 + nx*tmax, y1 + ny*tmax
end

local raycastMobj

rawset(_G, "raycastMobj", 0)
addHook("NetVars", function(sync)
	raycastMobj = sync($)
end)

mobjinfo[freeslot "MT_GREENRAY"] = {
	spawnstate = S_THOK,
	radius = FU,
	height = FU,
	flags = MF_NOGRAVITY|MF_NOCLIPTHING
}

local function XYZCollide(x, y, z, mo)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local halfrad = mo.radius
	local pos = mo.x //we're gonna be snobby and reuse this var for both x, y, and z to try and really squeeze out all the optimization 
	if x > pos+(halfrad) then return false end
	if x < pos-(halfrad) then return false end
	local pos = mo.y
	if y > pos+(halfrad) then return false end
	if y < pos-(halfrad) then return false end
	local pos = mo.z
	if z > mo.height+pos then return false end
	if z < pos then return false end
	return true
end

local function LineCollide(x, y, z, ox, oy, mo)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local mx = mo.x
	local my = mo.y
	local rad = mo.radius
	local mA = mo.x - rad //x 1
	local mB = mo.x + rad //x 2
	local mC = mo.y + rad //y
	local bruh = mo.z+mo.height
	if x >= mx-rad and x <= mx+rad and y >= my-rad and y <= my+rad then //if our coordinate is already in the hitbox we don't need to do math lmao
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	local mC = mo.y - rad //new y
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	local mA = mo.y - rad //y 1
	local mB = mo.y + rad //y 2
	local mC = mo.x + rad //x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	local mC = mo.x - rad //new x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	return false
end

local function XYLineCollide(x, y, ox, oy, mo)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local mx = mo.x
	local my = mo.y
	local rad = mo.radius
	local mA = mo.x - rad //x 1
	local mB = mo.x + rad //x 2
	local mC = mo.y + rad //y
	if x >= mx-rad and x <= mx+rad and y >= my-rad and y <= my+rad then return true end
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then return true end
	local mC = mo.y - rad //new y
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then return true end
	local mA = mo.y - rad //y 1
	local mB = mo.y + rad //y 2
	local mC = mo.x + rad //x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then return true end
	local mC = mo.x - rad //new x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then return true end
	return false
end

local function DoubleLineCollide(x, y, z, ox, oy, mo, size)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local mx = mo.x
	local my = mo.y
	local rad = mo.radius*size
	local mA = mx - rad //x 1
	local mB = mx + rad //x 2
	local mC = my + rad //y
	local bruh = mo.z+mo.height
	if x >= mx-rad and x <= mx+rad and y >= my-rad and y <= my+rad then //if our coordinate is already in the hitbox we don't need to do math lmao
		local pos = mo.z
		local mheight = mo.height
		if z > mheight+pos+(mheight*(size-1)/2) then return false end
		if z < pos-(mheight*(size-1)/2) then return false end
		return true
	end
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then
		local pos = mo.z
		local mheight = mo.height
		if z > mheight+pos+(mheight*(size-1)/2) then return false end
		if z < pos-(mheight*(size-1)/2) then return false end
		return true
	end
	local mC = my - rad //new y
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then
		local pos = mo.z
		local mheight = mo.height
		if z > mheight+pos+(mheight*(size-1)/2) then return false end
		if z < pos-(mheight*(size-1)/2) then return false end
		return true
	end
	local mA = my - rad //y 1
	local mB = my + rad //y 2
	local mC = mx + rad //x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then
		local pos = mo.z
		local mheight = mo.height
		if z > mheight+pos+(mheight*(size-1)/2) then return false end
		if z < pos-(mheight*(size-1)/2) then return false end
		return true
	end
	local mC = mx - rad //new x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then
		local pos = mo.z
		local mheight = mo.height
		if z > mheight+pos+(mheight*(size-1)/2) then return false end
		if z < pos-(mheight*(size-1)/2) then return false end
		return true
	end
	return false
end

local function DoubleXYLineCollide(x, y, ox, oy, mo, size)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local mx = mo.x
	local my = mo.y
	local rad = mo.radius * size
	local mA = mo.x - rad //x 1
	local mB = mo.x + rad //x 2
	local mC = mo.y + rad //y
	if x >= mx-rad and x <= mx+rad and y >= my-rad and y <= my+rad then return true end
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then return true end
	local mC = mo.y - rad //new y
	if intersect(ox,x,mA,mB,oy,y,mC,mC,true,true) then return true end
	local mA = mo.y - rad //y 1
	local mB = mo.y + rad //y 2
	local mC = mo.x + rad //x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then return true end
	local mC = mo.x - rad //new x
	if intersect(ox,x,mC,mC,oy,y,mA,mB,true,true) then return true end
	return false
end

/*local function LineCollide(x, y, z, ox, oy, mo)
	local flags = mo.flags
	if flags & (MF_NOCLIP|MF_NOCLIPTHING) then return false end
	if not (flags & MF_SHOOTABLE) then return false end
	local mx = mo.x
	local my = mo.y
	local rad = mo.radius
	local mA = mo.x - rad //x 1
	local mB = mo.x + rad //x 2
	local mC = mo.y + rad //y 1
	local mD = mo.y - rad //y 2
	local bruh = mo.z+mo.height
	P_SpawnMobj(ox, oy, z, MT_SMOKE)
	P_SpawnMobj(x, y, z, MT_SMOKE)
	P_SpawnMobj(mA, mC, bruh, MT_FLAMEPARTICLE)
	P_SpawnMobj(mB, mC, bruh, MT_FLAMEPARTICLE)
	if segmentVsAABB(ox,oy,x,y,mA,mC,mB,mD) then
		local pos = mo.z
		if z > mo.height+pos then return false end
		if z < pos then return false end
		return true
	end
	return false
end*/

rawset(_G, "ParseArg", function(args, variable, default)
	if not args then
		return default
	end

	if args[variable] == nil then
		return default
	end

	if default ~= nil
	and type(args[variable]) ~= type(default)then
		return default
	end

	return args[variable]
end)

rawset(_G, "P_AngleRaycast", function(args)
	// Arguments
	local x = ParseArg(args, "x", 0)
	local y = ParseArg(args, "y", 0)
	local z = ParseArg(args, "z", 0)
	local ang = ParseArg(args, "ang", 0)
	local zang = ParseArg(args, "zang", 0)
	local distance = ParseArg(args, "distance", 240*FU)
	local steps = ParseArg(args, "steps", 32)
	local callback = ParseArg(args, "callback")
	local callback2 = ParseArg(args, "callback2")
	local callback3 = ParseArg(args, "callback3")
	local arg = ParseArg(args, "arg")
	local sarg = ParseArg(args, "sarg")
	local larg = ParseArg(args, "larg")
	local tclip = ParseArg(args, "tclip")
	local lclip = ParseArg(args, "lclip")
	local ignore = ParseArg(args, "ignore")

	if not (raycastMobj and raycastMobj.valid) then
		raycastMobj = P_SpawnMobj(0,0,0, MT_GREENRAY)
	end

	local collisionlist = {}
	local collisionset = {}
	local vx = FixedMul(cos(ang), cos(zang))
	local vy = FixedMul(sin(ang), cos(zang))
	local vz = sin(zang)
	local ox
	local oy
	local oz

	local move = distance/steps
	
	//local bruhhhh = 0

	for stepcount = 1, steps do
		ox = x
		oy = y
		oz = z
		x = $ + FixedMul(move, vx)
		y = $ + FixedMul(move, vy)
		z = $ + FixedMul(move, vz)
		
		if callback then
			callback(x, y, z, arg)
		end
		
		if (raycastMobj and raycastMobj.valid) then
			P_SetOrigin(raycastMobj, x, y, z) //trouble area here, says the mobj doesnt exist
		end

		if searchBlockmap("objects", function(ref, object)
			if object == ignore then return nil end
			if collisionset[object] then return nil end
			if LineCollide(x, y, z, ox, oy, object) == true then
				table.insert(collisionlist, {x, y, z, object})
				collisionset[object] = true
				if callback3 then
					callback3(x, y, z, sarg, object)
				end
				if tclip == true or tclip == 1 then return true
				else return nil end
			end
		end, raycastMobj) == false then break end
	
		if lclip == true or lclip == 1 then
			local sect = raycastMobj.subsector.sector
			local floorheight = sect.floorheight
			local ceilheight = sect.ceilingheight
			if sect.f_slope then
				local slope = sect.f_slope
				local vectorx = x-slope.o.x //distance from origin x
				local vectory = y-slope.o.y //distance from origin y
				local angle = slope.xydirection //horizontal angle of slope
				local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
				local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
				floorheight = vectorz + slope.o.z
			end
			if sect.c_slope then
				local slope = sect.c_slope
				local vectorx = x-slope.o.x //distance from origin x
				local vectory = y-slope.o.y //distance from origin y
				local angle = slope.xydirection //horizontal angle of slope
				local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
				local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
				ceilheight = vectorz + slope.o.z
			end
			if z < floorheight or z > ceilheight then //in floor or ceiling
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
			local shouldbreak = 0
			for fof in sect.ffloors() do //check fofs
				if not (fof.flags & FOF_SOLID) then continue end
				if not (fof.flags & FOF_EXISTS) then continue end
				local topheight = fof.topheight
				local bottomheight = fof.bottomheight
				if fof.t_slope then
					local slope = fof.t_slope
					local vectorx = x-slope.o.x //distance from origin x
					local vectory = y-slope.o.y //distance from origin y
					local angle = slope.xydirection //horizontal angle of slope
					local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
					local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
					topheight = vectorz + slope.o.z
				end
				if fof.b_slope then
					local slope = fof.b_slope
					local vectorx = x-slope.o.x //distance from origin x
					local vectory = y-slope.o.y //distance from origin y
					local angle = slope.xydirection //horizontal angle of slope
					local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
					local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
					bottomheight = vectorz + slope.o.z
				end
				if (z <= topheight and z >= bottomheight) then
					shouldbreak = 1
					break
				end
			end
			if shouldbreak == 1 then
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
			if R_PointInSubsectorOrNil(x, y) == nil then //check walls
				//bruhhhh = $ + 1
				if callback2 then //stjr fix your fucking function brahhhhhh, this returns nil over linedefs.
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
		end
	end
	P_RemoveMobj(raycastMobj)
	return collisionlist
end)

rawset(_G, "P_IterateRaycast", function(args)
	// Arguments
	local x = ParseArg(args, "x", 0)
	local y = ParseArg(args, "y", 0)
	local z = ParseArg(args, "z", 0)
	local ang = ParseArg(args, "ang", 0)
	local zang = ParseArg(args, "zang", 0)
	local distance = ParseArg(args, "distance", 240*FU)
	local steps = ParseArg(args, "steps", 32)
	local callback = ParseArg(args, "callback")
	local callback2 = ParseArg(args, "callback2")
	local callback3 = ParseArg(args, "callback3")
	local arg = ParseArg(args, "arg")
	local sarg = ParseArg(args, "sarg")
	local larg = ParseArg(args, "larg")
	local tclip = ParseArg(args, "tclip")
	local lclip = ParseArg(args, "lclip")
	local ignore = ParseArg(args, "ignore")

	if not (raycastMobj and raycastMobj.valid) then
		raycastMobj = P_SpawnMobj(0,0,0, MT_GREENRAY)
	end

	local collisionlist = {}
	local collisionset = {}
	local vx = FixedMul(cos(ang), cos(zang))
	local vy = FixedMul(sin(ang), cos(zang))
	local vz = sin(zang)
	local ox
	local oy
	local oz

	local move = distance/steps
	
	//local bruhhhh = 0

	for stepcount = 1, steps do
		ox = x
		oy = y
		oz = z
		x = $ + FixedMul(move, vx)
		y = $ + FixedMul(move, vy)
		z = $ + FixedMul(move, vz)
		
		if callback then
			callback(x, y, z, arg, nil, stepcount)
		end
		
		if (raycastMobj and raycastMobj.valid) then
			P_SetOrigin(raycastMobj, x, y, z) //trouble area here, says the mobj doesnt exist
		end

		if searchBlockmap("objects", function(ref, object)
			if object == ignore then return nil end
			if collisionset[object] then return nil end
			if LineCollide(x, y, z, ox, oy, object) == true then
				table.insert(collisionlist, {x, y, z, object})
				collisionset[object] = true
				if callback3 then
					callback3(x, y, z, sarg, object, stepcount)
				end
				if tclip == true or tclip == 1 then return true
				else return nil end
			end
		end, raycastMobj) == false then break end
	
		if lclip == true or lclip == 1 then
			local sect = raycastMobj.subsector.sector
			local floorheight = sect.floorheight
			local ceilheight = sect.ceilingheight
			if sect.f_slope then
				local slope = sect.f_slope
				local vectorx = x-slope.o.x //distance from origin x
				local vectory = y-slope.o.y //distance from origin y
				local angle = slope.xydirection //horizontal angle of slope
				local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
				local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
				floorheight = vectorz + slope.o.z
			end
			if sect.c_slope then
				local slope = sect.c_slope
				local vectorx = x-slope.o.x //distance from origin x
				local vectory = y-slope.o.y //distance from origin y
				local angle = slope.xydirection //horizontal angle of slope
				local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
				local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
				ceilheight = vectorz + slope.o.z
			end
			if z < floorheight or z > ceilheight then //in floor or ceiling
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg, nil, stepcount)
				end
				break
			end
			local shouldbreak = 0
			for fof in sect.ffloors() do //check fofs
				if not (fof.flags & FOF_SOLID) then continue end
				if not (fof.flags & FOF_EXISTS) then continue end
				local topheight = fof.topheight
				local bottomheight = fof.bottomheight
				if fof.t_slope then
					local slope = fof.t_slope
					local vectorx = x-slope.o.x //distance from origin x
					local vectory = y-slope.o.y //distance from origin y
					local angle = slope.xydirection //horizontal angle of slope
					local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
					local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
					topheight = vectorz + slope.o.z
				end
				if fof.b_slope then
					local slope = fof.b_slope
					local vectorx = x-slope.o.x //distance from origin x
					local vectory = y-slope.o.y //distance from origin y
					local angle = slope.xydirection //horizontal angle of slope
					local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
					local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
					bottomheight = vectorz + slope.o.z
				end
				if (z <= topheight and z >= bottomheight) then
					shouldbreak = 1
					break
				end
			end
			if shouldbreak == 1 then
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg, nil, stepcount)
				end
				break
			end
			if R_PointInSubsectorOrNil(x, y) == nil then //check walls
				//bruhhhh = $ + 1
				if callback2 then //stjr fix your fucking function brahhhhhh, this returns nil over linedefs.
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg, nil, stepcount)
				end
				break
			end
		end
	end
	P_RemoveMobj(raycastMobj)
	return collisionlist
end)

rawset(_G, "P_MagnetizedAngleRaycast", function(args)
	// Arguments
	local x = ParseArg(args, "x", 0)
	local y = ParseArg(args, "y", 0)
	local z = ParseArg(args, "z", 0)
	local ang = ParseArg(args, "ang", 0)
	local zang = ParseArg(args, "zang", 0)
	local distance = ParseArg(args, "distance", 240*FU)
	local steps = ParseArg(args, "steps", 32)
	local callback = ParseArg(args, "callback")
	local callback2 = ParseArg(args, "callback2")
	local callback3 = ParseArg(args, "callback3")
	local arg = ParseArg(args, "arg")
	local sarg = ParseArg(args, "sarg")
	local larg = ParseArg(args, "larg")
	local tclip = ParseArg(args, "tclip")
	local lclip = ParseArg(args, "lclip")
	local size = ParseArg(args, "size") //multiplier, 2 will magnetize to twice the normal hitbox, 3 will magnetize to three times the normal hitbox, so on.
	local ignore = ParseArg(args, "ignore")

	if not (raycastMobj and raycastMobj.valid) then
		raycastMobj = P_SpawnMobj(0,0,0, MT_GREENRAY)
	end

	local collisionlist = {}
	local collisionset = {}
	local vx = FixedMul(cos(ang), cos(zang))
	local vy = FixedMul(sin(ang), cos(zang))
	local vz = sin(zang)
	local ox
	local oy
	local oz

	local move = distance/steps

	for stepcount = 1, steps do
		ox = x
		oy = y
		oz = z
		x = $ + FixedMul(move, vx)
		y = $ + FixedMul(move, vy)
		z = $ + FixedMul(move, vz)
		
		if callback then
			callback(x, y, z, arg)
		end
		
		if (raycastMobj and raycastMobj.valid) then
			P_SetOrigin(raycastMobj, x, y, z) //trouble area here, says the mobj doesnt exist
		end

		if searchBlockmap("objects", function(ref, object)
			//if object.type ~= MT_PLAYER then return nil end
			if object == ignore then return nil end
			if collisionset[object] then return nil end
			if DoubleLineCollide(x, y, z, ox, oy, object, size) == true then
				table.insert(collisionlist, {x, y, z, object})
				collisionset[object] = true
				if callback3 then
					callback3(x, y, z, sarg, object)
				end
				if tclip == true or tclip == 1 then return true
				else return nil end
			end
		end, raycastMobj, raycastMobj.x - 128*FU, raycastMobj.x + 128*FU, raycastMobj.y - 128*FU, raycastMobj.y + 128*FU) == false then break end //the xxyy values are a hacky fix for an issue too long to explain in a single line comment.
//, raycastMobj.x - 128*FU*(size-1), raycastMobj.x + 128*FU*(size-1), raycastMobj.y - 128*FU*(size-1), raycastMobj.y + 128*FU*(size-1)
		if lclip == true or lclip == 1 then
			local sect = raycastMobj.subsector.sector
			local floorheight = sect.floorheight
			local ceilheight = sect.ceilingheight
			if sect.f_slope then
				local slope = sect.f_slope
				local vectorx = x-slope.o.x //distance from origin x
				local vectory = y-slope.o.y //distance from origin y
				local angle = slope.xydirection //horizontal angle of slope
				local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
				local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
				floorheight = vectorz + slope.o.z
			end
			if sect.c_slope then
				local slope = sect.c_slope
				local vectorx = x-slope.o.x //distance from origin x
				local vectory = y-slope.o.y //distance from origin y
				local angle = slope.xydirection //horizontal angle of slope
				local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
				local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
				ceilheight = vectorz + slope.o.z
			end
			if z < floorheight or z > ceilheight then //in floor or ceiling
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
			local shouldbreak = 0
			for fof in sect.ffloors() do
				if not (fof.flags & FOF_SOLID) then continue end
				if not (fof.flags & FOF_EXISTS) then continue end
				local topheight = fof.topheight
				local bottomheight = fof.bottomheight
				if fof.t_slope then
					local slope = fof.t_slope
					local vectorx = x-slope.o.x //distance from origin x
					local vectory = y-slope.o.y //distance from origin y
					local angle = slope.xydirection //horizontal angle of slope
					local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
					local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
					topheight = vectorz + slope.o.z
				end
				if fof.b_slope then
					local slope = fof.b_slope
					local vectorx = x-slope.o.x //distance from origin x
					local vectory = y-slope.o.y //distance from origin y
					local angle = slope.xydirection //horizontal angle of slope
					local dist = FixedMul(vectorx, cos(angle))+FixedMul(vectory, sin(angle)) //distance from origin
					local vectorz = FixedMul(tan(slope.zangle), dist) //the slope's distance from origin z at this specific part of the slope
					bottomheight = vectorz + slope.o.z
				end
				if (z <= topheight and z >= bottomheight) then
					shouldbreak = 1
					break
				end
			end
			if shouldbreak == 1 then
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
			if R_PointInSubsectorOrNil(x, y) == nil then
				if callback2 then
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
		end
	end
	P_RemoveMobj(raycastMobj)
	return collisionlist
end)

rawset(_G, "P_XYRaycast", function(args)
	// Arguments
	local x = ParseArg(args, "x", 0)
	local y = ParseArg(args, "y", 0)
	local ang = ParseArg(args, "ang", 0)
	local distance = ParseArg(args, "distance", 240*FU)
	local steps = ParseArg(args, "steps", 32)
	local callback = ParseArg(args, "callback")
	local callback2 = ParseArg(args, "callback2")
	local callback3 = ParseArg(args, "callback3")
	local arg = ParseArg(args, "arg")
	local sarg = ParseArg(args, "sarg")
	local larg = ParseArg(args, "larg")
	local tclip = ParseArg(args, "tclip")
	local lclip = ParseArg(args, "lclip")
	local ignore = ParseArg(args, "ignore")

	if not (raycastMobj and raycastMobj.valid) then
		raycastMobj = P_SpawnMobj(0,0,0, MT_GREENRAY)
	end

	local collisionlist = {}
	local collisionset = {}
	local vx = cos(ang)
	local vy = sin(ang)
	local ox
	local oy

	local move = distance/steps
	
	//local bruhhhh = 0

	for stepcount = 1, steps do
		ox = x
		oy = y
		x = $ + FixedMul(move, vx)
		y = $ + FixedMul(move, vy)
		
		if callback then
			callback(x, y, 0, arg)
		end
		
		if (raycastMobj and raycastMobj.valid) then
			P_SetOrigin(raycastMobj, x, y, 0) //trouble area here, says the mobj doesnt exist
		end

		if searchBlockmap("objects", function(ref, object)
			if object == ignore then return nil end
			if collisionset[object] then return nil end
			if XYLineCollide(x, y, ox, oy, object) == true then
				table.insert(collisionlist, {x, y, object})
				collisionset[object] = true
				if callback3 then
					callback3(x, y, 0, sarg, object)
				end
				if tclip == true or tclip == 1 then return true
				else return nil end
			end
		end, raycastMobj) == false then break end
	
		if lclip == true or lclip == 1 then
			if R_PointInSubsectorOrNil(x, y) == nil then //check walls
				//bruhhhh = $ + 1
				if callback2 then //stjr fix your fucking function brahhhhhh, this returns nil over linedefs.
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
		end
	end
	P_RemoveMobj(raycastMobj)
	return collisionlist
end)

rawset(_G, "P_MagnetizedXYRaycast", function(args)
	// Arguments
	local x = ParseArg(args, "x", 0)
	local y = ParseArg(args, "y", 0)
	local ang = ParseArg(args, "ang", 0)
	local distance = ParseArg(args, "distance", 240*FU)
	local steps = ParseArg(args, "steps", 32)
	local callback = ParseArg(args, "callback")
	local callback2 = ParseArg(args, "callback2")
	local callback3 = ParseArg(args, "callback3")
	local arg = ParseArg(args, "arg")
	local sarg = ParseArg(args, "sarg")
	local larg = ParseArg(args, "larg")
	local tclip = ParseArg(args, "tclip")
	local lclip = ParseArg(args, "lclip")
	local size = ParseArg(args, "size") //multiplier, 2 will magnetize to twice the normal hitbox, 3 will magnetize to three times the normal hitbox, so on.
	local ignore = ParseArg(args, "ignore")

	if not (raycastMobj and raycastMobj.valid) then
		raycastMobj = P_SpawnMobj(0,0,0, MT_GREENRAY)
	end

	local collisionlist = {}
	local collisionset = {}
	local vx = cos(ang)
	local vy = sin(ang)
	local ox
	local oy

	local move = distance/steps
	
	//local bruhhhh = 0

	for stepcount = 1, steps do
		ox = x
		oy = y
		x = $ + FixedMul(move, vx)
		y = $ + FixedMul(move, vy)
		
		if callback then
			callback(x, y, 0, arg)
		end
		
		if (raycastMobj and raycastMobj.valid) then
			P_SetOrigin(raycastMobj, x, y, 0) //trouble area here, says the mobj doesnt exist
		end

		if searchBlockmap("objects", function(ref, object)
			if object == ignore then return nil end
			if collisionset[object] then return nil end
			if DoubleXYLineCollide(x, y, ox, oy, object, size) == true then
				table.insert(collisionlist, {x, y, object})
				collisionset[object] = true
				if callback3 then
					callback3(x, y, 0, sarg, object)
				end
				if tclip == true or tclip == 1 then return true
				else return nil end
			end
		end, raycastMobj) == false then break end
	
		if lclip == true or lclip == 1 then
			if R_PointInSubsectorOrNil(x, y) == nil then //check walls
				//bruhhhh = $ + 1
				if callback2 then //stjr fix your fucking function brahhhhhh, this returns nil over linedefs.
					callback2(x-FixedMul(move, vx), y-FixedMul(move, vy), z-FixedMul(move, vz), larg or sarg)
				end
				break
			end
		end
	end
	P_RemoveMobj(raycastMobj)
	return collisionlist
end)

//COM_AddCommand("raytest", function(bruh, x, y, z, ang, zang, step, amount)
//	P_AngleRaycast(tonumber(x)*FU, tonumber(y)*FU, tonumber(z)*FU, FixedAngle(tonumber(ang)*FU), FixedAngle(tonumber(zang)*FU), tonumber(step), tonumber(amount), P_SpawnMobj, nil, MT_THOK, nil, true, true)
//end, COM_ADMIN)